Return to doc.sitecore.com

Building Web Controls - Part 1 (FieldViewer control)

  Part1 - Introduction to Sitecore.Web.UI.WebControl

1.  Introduction

ASP.NET is based on control based architecture where any ASP.NET page is a control containing child controls. Developers can proclaim re-usability by creating their own custom controls derived from System.Web.UI.Control or System.Web.UI.WebControl and thus gradually extend their library of reusable controls. The most appealing feature of custom controls is that their usage is the same as that of intrinsic controls built into ASP.NET (such as the Label and TextBox control).

In order to give Sitecore developers the benefits of custom controls while adding value by supporting CMS directly, the System.Web.UI.WebControl class has been extended with a number of methods and properties to hook into Sitecore CMS.

2.  Sitecore.Web.UI.WebControl

Any class derived from System.Web.Control is a control that can be used inside Visual Studio with full IDE support. Every control in ASP.NET including the page itself derives either directly or indirectly from this class.

When creating custom controls, one can either derive directly from System.Web.Control or from System.Web.WebControl. The latter exposes additional properties such as BorderColor and CssClass.
Sitecore has developed a class (Sitecore.Web.UI.WebControl) that derives directly from System.Web.WebControl. It provides quite a few additional methods and properties useful when developing for Sitecore.

2.1.  Web control source

1 public abstract class WebControl : System.Web.UI.WebControls.WebControl, ISupportInitialize {
2     
3     protected enum ContentGetter {
4       InnerHtml,
5       InnerText,
6       Value
7     }
8
9     protected void MakeEditable(HtmlControl control, string field);
10     protected void MakeEditable(HtmlControl control, string field, ContentGetter getter);
11     protected void MakeEditable(HtmlControl control, string field, string getMethod);
12     public virtual string Parameters {get; set;}
13     public virtual void UpdateDataSource();
14     public void SetDataSource(string sDataSource);
15     public void SetDesignSource(string sDesignSource)
16     public void PropertyChanged(string sProperty, object oldvalue, object newvalue);
17
18     //Apperance
19     public virtual string Background {get; set;}
20     public virtual string Border {get; set;}
21     public virtual bool Bordered {get; set;}
22     public virtual string Clear {get; set;}
23     public virtual string CssStyle {get; set;}
24     public virtual string Cursor {get; set;}
25     public virtual string Margin {get; set;}
26     public virtual string Foreground {get; set;}
27     public virtual string Padding {get; set;}
28     public virtual string DataSource {get; set;}
29     public virtual string DesignSource {get; set;}
30     public virtual bool DesignMode {get;}
31     public virtual bool DisableDebug {get; set;}
32     public virtual bool DisableDots {get; set;}
33     public virtual bool DrawDots {get;}
34     public virtual string Float {get; set;}
35     public virtual bool LiveDisplay {get; set;}
36     public virtual string LoginRendering {get; set;}
37     
38     //Designtime
39     public virtual string Connection {get; set;}
40     public virtual string GetDesignTimeHtml(XmlNode xml);
41     
42     //Cache
43     public virtual bool Cacheable {get; set;}
44     public virtual bool VaryByData {get; set;}
45     public virtual bool VaryByDevice {get; set;}
46     public virtual bool VaryByLogin {get; set;}
47     public virtual bool VaryByParm {get; set;}
48     public virtual bool VaryByQueryString {get; set;}
49     public virtual bool VaryByUser {get; set;}
50     public virtual string GetCacheKey() {}
51     protected virtual void AddToCache(string cacheKey, string html);
52     protected virtual string GetCachingID();
53
54     //Rendering
55     public virtual string RenderControl(XmlNode properties);
56     public void RenderError(string errorMessage, string details, HtmlTextWriter output);
57     public virtual void RenderXml(XmlNode xml, TextWriter output);
58     protected virtual void AfterRender(HtmlTextWriter output);
59     protected virtual void BeforeRender(HtmlTextWriter output);
60     protected abstract void DoRender(HtmlTextWriter output);
61     protected override void Render(HtmlTextWriter output);
62     protected bool RenderFromCache(string cacheKey, HtmlTextWriter output);
63     
64     //ISupportInitialize methods
65     void ISupportInitialize.BeginInit();
66     void ISupportInitialize.EndInit();
67     
68     //Utility methods
69     protected virtual Item GetItem();
70     public virtual void SetControlAttribute(string attribute, string _value, HtmlControl control);
71     protected virtual Item GetAncestorItem(string[] vTemplates, Item itm);
72     public static Control GetErrorControl(string errorMessage, string details);
73     protected virtual string GetFieldValue(string field);
74     protected void TransferAttributes(Control control);
75     
76     //Reserved for internal use only
77     protected virtual void EmitRenderingInfoScript();
78     public virtual Hashtable GetExtensibleProperties(Control control);
79     public virtual string GetExtensibleProperty(Control control, string name);
80     public virtual string GetTraceName();
81     public virtual void SetExtensibleProperty(Control control, string name, string value);
82 }

3.  GetItem method

For this article, only the utility method GetItem() will be used. The other methods and properties will be covered in other articles of this series.

An Item in Sitecore has a layout attached to it (can be set in the content editor of the Sitecore client). A layout is basically an .html page which can be edited in Visual Studio as any other .html page with code behind and so fourth. Because it is an ordinary .html page, it can contain custom controls just as any other .html page.

For example, if you want to display an Item with the MyItem name located at the root of your web, you would write something like this in the address bar of your browser:

http://localhost/MyItem.html

If the MyLayout.html layout is attached to the Item in question, this layout will be rendered back to the client. If the layout contains a WebControl custom, the GetItem() method of that control will return the requested Item.

To illustrate, let us create the WebControl custom (see the code).

The FieldViewer class derives from Sitecore.Web.UI.WebControl and contains only one property: FieldName of type string.

An Item has a collection of Fields, where developers can store data. Amongst common fields are Title and Text fields, but other fields could also exist. For example, Image and Blob. The control created for this example will be able to render all field values and will access the field values in their raw string format.

The DoRender() method derives from System.Web.UI.Control and is called when a control is to be rendered. To render a control, the output from your control must be added to the HtmlTextWriter buffer. The FieldViewer class first retrieves the current Item in line 12 and simply outputs whatever is located in the field with the name given in the FieldName property to the HtmlTextWriter buffer.

3.1.  FieldViewer control

1 public class FieldViewer  : WebControl {
2   
3     private string _fieldName;
4
5     [WebControlProperty]
6     public string FieldName {
7       get {return _fieldName;}
8       set {_fieldName = value;}
9     }
10
11     protected override void DoRender(HtmlTextWriter output) {
12       Item item = GetItem();
13       output.Write(item.Fields[_fieldName]);
14     }
15 }

4.  Text WebControl

For this section, the Text WebControl is used as an example. The FieldViewer example used the Fields collection of the Item to retrieve the value of the field. The object returned by item.Fields[_fieldName] is of the Field type and when outputting the Field to the buffer, the ToString() method was implicitly called. The ToString() method of the Field class returns the Value property of the Field class and is therefore equal to the expression item.Fields[_fieldname].ToString().

There are at least four different ways of retrieving field value:  

item.Fields[_fieldName].ToString();

item.Fields[_fieldName].Value;

item[_fieldName];

GetFieldValue(_fieldName);

The GetFiedValue(string fieldName) method gets the current Item using GetItem() and returns the value of the field given as parameter. It is a convenience method derived from Sitecore.Web.UI.WebControl which supplies access to current Items’ field values in one method call. Where the FieldViewer control uses the item.Fields[_fieldName].ToString() approach, the Text WebControl uses the GetFieldValue() approach. The result is exactly the same.

The item[_fieldName] property is a wrapper for the item.Fields[_fieldName] value created for shorter expressions.

Apart from using different strategies to retrieve Field values, the Text control expands the FieldViewer by adding additional div tags encapsulating the value in order to support stylesheets.

The best way to add content to the HtmlTextWriter buffer is to use the stack based syntax as shown in the Text WebControl DoRender() method. The reason for this is that indentation of the rendered HTML is performed implicitly by ASP.NET when you use the stack-based method. Thus manual indention of the HTML is avoided, and ASP has no possibility to alter the output based on the capabilities of the used browser if manual output is used.

4.1.  Text WebControl DoRender

1 protected override void DoRender(HtmlTextWriter output) {
2       if (ClassAttribute.Length > 0)
3         output.AddAttribute(HtmlTextWriterAttribute.Class, ClassAttribute);
4       if (StyleAttribute.Length > 0)
5         output.AddAttribute(HtmlTextWriterAttribute.Style, StyleAttribute);
6       output.RenderBeginTag(HtmlTextWriterTag.Div);
7       
8       string val = string.Empty;
9        if(_text.Length == 0)
10        {
11           val = GetFieldValue(_textField);
12        }
13        else
14        {
15          val = _text;
16        }
17
18       output.AddAttribute(HtmlTextWriterAttribute.Class, TextClass);
19       output.AddAttribute(HtmlTextWriterAttribute.Style, TextStyle);
20       output.RenderBeginTag(HtmlTextWriterTag.Div);
21       output.Write(val);
22       output.RenderEndTag();
23       output.RenderEndTag();
24     }
25
26     #endregion
27   }
28 }

5.  Image WebControl

The Image WebControl introduces two new constructs: The ImageField and the MediaItem. As explained earlier, the type of the expression item.Fields[_FieldName] is Field. If you know that the content of the retrieved Field is an Image, you can make use of the implicit conversion from a Field type to a ImageField type (see code).

A Field should be thought of as an descriptor of the relations between Items in this example between an Item and and an Item which is an image (ImageItem). The ImageField describes how the ImageItem is positioned in the parent Item and has therefor properties such as Alt and Height. This implies that ImageItems can be used in many different places with different Alt and Height attributes because it is the Field that describes how it is inserted.

5.1.  ImageField

1 public class ImageField : XmlField {
2   
3     public static implicit operator ImageField(Field field);
4     
5     public string Alt {get; set;}
6     public string Class {get; set}
7     public string Height {get; set;}
8     public string HSpace {get; set;}
9     public string InternalPath {get; set;}
10     public bool IsInternal {get;}
11     public string LinkType {get; set;}
12     public ID MediaID {get; set; } //id of media Item
13     public Item MediaItem {get;}
14     public string Src {get; set;}
15     public string VSpace {get; set;}
16     public string Width {get; set;}
17     
18     public void Clear();
19     public void Relink(Item item, Item newLink);
20     public void RemoveLink(Item item);
21   }

5.2.  Image WebControl DoRender

1 protected override void DoRender(HtmlTextWriter output)
2       {
3       
4          Item itm = GetItem();
5       
6          if (itm != null)
7          {  
8             Field field  = itm.Fields[ImageField];
9             if(field!=null)
10             {
11                ImageField imageField = field;
12                MediaItem mediaItem = imageField.MediaItem;
13         
14                if (mediaItem != null)
15                {
16           
17                   if(_link)
18                   {
19                      output.AddAttribute(HtmlTextWriterAttribute.Href, this._linkUrl);
20                      if(_linkNewWindow)
21                         output.AddAttribute(HtmlTextWriterAttribute.Target, "_blank");
22                      output.RenderBeginTag(HtmlTextWriterTag.A) ;
23                   }
24
25                   output.AddAttribute(HtmlTextWriterAttribute.Src, mediaItem.Path) ;
26                   if(imageField.Alt != string.Empty || _alt != string.Empty )
27                      output.AddAttribute(HtmlTextWriterAttribute.Alt,
28                         _alt == string.Empty ? imageField.Alt : _alt);
29                   if(!Width.IsEmpty || imageField.Height != string.Empty)
30                      output.AddAttribute(HtmlTextWriterAttribute.Width,
31                         Width.IsEmpty ? imageField.Width : Width.ToString());
32                   if(!Height.IsEmpty || imageField.Height != string.Empty)
33                      output.AddAttribute(HtmlTextWriterAttribute.Height,
34                         Height.IsEmpty ? imageField.Height : Height.ToString());
35                   if((Border != null) && (Border != string.Empty))
36                      output.AddAttribute(HtmlTextWriterAttribute.Border, Border.ToString());
37                   if(imageField.VSpace != string.Empty || _vspace != string.Empty)
38                      output.AddAttribute("vspace",
39                         imageField.VSpace == string.Empty ? _vspace : imageField.VSpace);
40                   if(imageField.HSpace != string.Empty || _hspace != string.Empty)
41                      output.AddAttribute("hspace",
42                         imageField.HSpace == string.Empty ? _hspace : imageField.HSpace);
43
44                   output.RenderBeginTag(HtmlTextWriterTag.Img); //<img>        
45                   output.RenderEndTag();                        //</img>
46                   if(_link)
47                      output.RenderEndTag();
48                }
49             }
50          }
51       }

6.  Link WebControl

The Link WebControl creates an <a> anchor of the Items’ link-field and emits it to the HtmlTextWriter output buffer. It introduces an alternative way of rendering controls. Up until now the stack-based way of rendering has been taken, but developers can also instantiate controls directly, manipulate them programmatically and render them to the output buffer (see the DoRender method of the Link Control). The Link WebControl uses a mix of the stack-based method and the object based method.

It also introduces the WebUtil class to create an anchor: WebUtil.GetAnchor(Item item, string linkField, sting text). The WebUtil class is a convenience class supplied to Sitecore for common methods used when creating WebControls.

6.1.  Link DoRender

1 protected override void DoRender(HtmlTextWriter output) {
2       output.AddAttribute(HtmlTextWriterAttribute.Class, ClassAttribute);
3       output.AddAttribute(HtmlTextWriterAttribute.Style, StyleAttribute) ;
4       output.RenderBeginTag(HtmlTextWriterTag.Div);
5       Item itm = GetItem();
6       if (itm != null) {
7         string text = itm.Fields[_textField].Value;
8         HtmlAnchor anchor = WebUtil.GetAnchor(itm, _linkField, text);
9         if (anchor != null) {
10           if (TextClass.Length > 0) {
11             anchor.Attributes["class"] = TextClass;
12           }
13           if (TextStyle.Length > 0) {
14             anchor.Attributes["style"] = TextStyle;
15           }
16           anchor.RenderControl(output);
17         }
18       }
19       output.RenderEndTag();
20     }

7.  Breadcrumb WebControl

The Breadcrumb WebControl is a bit more complex than the other controls investigated up until now. Its purpose is to create links from the root Item of your site to a current Item. In the style:

MyRoot | Level1 | Level2

Where the separators can be images or ordinary text.

In order to retrieve the root Item of your site, the Breadcrumb control uses:

Sitecore.Context.Database.Items[“/sitecore/content”]

The Sitecore.Context object contains information regarding the current configuration of Sitecore, including the currently used Database. The Database object can in this way be used to query Items as shown resulting in the root Item of your content.

8.  Compiling the control

In order to properly compile the control, one must add a reference to Sitecore.Kernel.dll found in the bin directory of Sitecore and compile it using the C# compiler:

csc /t:library /r:Sitecore.Kernel.dll FieldViewer.cs

The FieldViewer can also be compiled using Visual Studio .NET by creating a new class library, adding the FieldViever source file, adding a reference to Sitecore.Kernel.dll and compiling by pressing Ctrl+Shift+B

9.  Using the control from Visual Studio

You should first setup Sitecore in Visual Studio as discussed in the article Using Visual Studio .NET.

10.  Conclusion

This article discusses what an WebControl is and how the Sitecore.Web.UI.WebControl provides a hook into Sitecore CMS.

Download the Sample Codes.